(ns acst.models.forms-constraint
  (:use acst.config.error-config
        acst.config.success-config
        acst.utils.handle-exp)
  (:require [acst.models.db :as db]
            [acst.utils.sql-util :as sqlutil]
            [clojure.core.match :refer :all]
            [clojure.data.json :as json]
            [clojure.set :refer [union]]))


(defn- valid-length? [longer shorter]
  (try
    (match [longer shorter]
           [nil nil] true
           [nil _] (> (Integer. shorter) 0)
           [_ nil] (> (Integer. longer) 0)
           [_ _] (and (< (Integer. longer) (Integer. shorter)) (> (Integer. longer) 0))
           :else false)
    (catch Exception e
      false)))

(defn- valid-notnull? [notnull]
  (or (= "true" notnull)
      (= "false" notnull)
      (true? notnull)
      (false? notnull)
      (nil? notnull)))

(defn- valid-column? [column]
  (and (not (nil? column)) (re-matches #"[0-9A-Za-z\-\_]+" column)))

(defn- valid-desc? [desc]
  (not (or (nil? desc) (< (count desc) 1) (re-find #"[\<\>\"\\\&\s]" desc))))

(defn- valid-regex [regex]
  (try
    (or (nil? regex)
        (re-pattern regex))
    (catch Exception ex
      false)))

(defn- valid-keys? [constraint]
  ;求定义结构的键和允许的键的并集是否等于允许的键
  (= (union #{:desc :notnull :longer :shorter :regex} (into #{} (keys constraint))) #{:desc :notnull :longer :shorter :regex}))


(defn- find-constraint-error [{:keys [desc notnull longer shorter regex] :as all}]
  (->> (if-not (valid-desc? desc) (:invalid-desc forms-constraint-error) nil)
       (if-not (valid-notnull? notnull) (:invalid-notnull forms-constraint-error))
       (if-not (valid-length? longer shorter) (:invalid-length forms-constraint-error))
       (if-not (valid-regex regex) (:invalid-regex forms-constraint-error))
       (if-not (valid-keys? all) (:invalid-key forms-constraint-error))))

(defn- convert-constraint [{:keys [longer shorter notnull] :as form_constraint}]
  (let [a (if (nil? longer) 0 (Integer. longer))            ;;最短长度是0
        b (if (nil? shorter) 1024 (Integer. shorter))]      ;;最长1024...
    (->
      (assoc form_constraint :longer a)
      (assoc :shorter b)
      (assoc :notnull (if (or (= "true" notnull) (true? notnull)) true false)))))

;(defn valid-convert-constraint
;  ([form_constraint] (valid-convert-constraint form_constraint #{} []))
;  ([form_constraint column-set converd-constraint]
;    (if (= '() form_constraint)
;      converd-constraint
;      (let [coved-constraint (convert-constraint (first form_constraint))
;            {column :column :as result} (valid-constraint? coved-constraint)]
;        (if (and column (nil? (column-set column)))
;          (recur (rest form_constraint) (conj column-set column) (conj converd-constraint coved-constraint))
;          (if (result :column) (:invalid-unique-name forms-constraint-error) result))))))

(defn valid-convert-constraint
  ([form_constraint]
   (if (map? form_constraint)
     (valid-convert-constraint form_constraint (keys form_constraint) {})
     (:not-a-map forms-constraint-error)))
  ([form_constraint columns result]
   (if (= '() columns)
     result
     (if (not (valid-column? (name (first columns))))
       (:invalid-name forms-constraint-error)
       (let [column-name (first columns)
             constraint (get form_constraint column-name)
             error (find-constraint-error constraint)]
         (if (nil? error)
           (recur form_constraint (rest columns) (conj result {column-name (convert-constraint constraint)}))
           error))))))


(defn insert-constraint! [user_id activity_id form_constraint]              ;;调用者需要加admin_require
  (let [converd-constraint (valid-convert-constraint form_constraint)]
    (if (get-error converd-constraint)
      converd-constraint
      (if (get-error (db/insert! :forms_constraint {:publisher_id user_id :activity_id activity_id :form_constraint converd-constraint}))
        (:create-form-constraint-faile forms-constraint-error)
        (:create-form-constraint-success forms-constraint-result)))))

(defn update-constraint! [user_id activity_id form_constraint]              ;;调用者需要加admin_require
  (let [converd-constraint (valid-convert-constraint form_constraint)]
    (if (get-error converd-constraint)
      converd-constraint
      (sqlutil/handle-update!
        user_id
        :forms_constraint
        {:form_constraint converd-constraint}
        ["activity_id = ? AND publisher_id = ?" activity_id user_id]
        ["activity_id = ?" activity_id]
        (:update-form-constraint-success forms-constraint-result)
        (:update-form-constraint-faile forms-constraint-error)))))

(defn delete-constraint! [activity_id user_id]
  (sqlutil/handle-delete!
    user_id
    :forms_constraint
    ["activity_id = ? AND publisher_id = ?" activity_id user_id]
    ["activity_id = ?" activity_id]
    (:delete-form-constraint-success forms-constraint-result)
    (:delete-form-constraint-failed forms-constraint-error)))


(defn get-constraint [activity_id]
  (db/query (:constraint-not-found forms-constraint-error) ["SELECT form_constraint FROM forms_constraint WHERE activity_id = ?" activity_id]))